package com.eshop.modules.mp.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eshop.api.BusinessException;
import com.eshop.api.EshopException;
import com.eshop.enums.*;
import com.eshop.modules.mp.config.WxPayConfiguration;
import com.eshop.modules.order.service.StoreOrderService;
import com.eshop.modules.order.vo.StoreOrderQueryVo;
import com.eshop.modules.user.domain.ShopUser;
import com.eshop.modules.user.domain.UserRecharge;
import com.eshop.modules.user.service.UserRechargeService;
import com.eshop.modules.user.service.UserService;
import com.eshop.modules.user.service.dto.WechatUserDto;
import com.eshop.utils.IpUtil;
import com.eshop.utils.RedisUtils;
import com.eshop.utils.ShopKeyUtils;
import com.github.binarywang.wxpay.bean.entpay.EntPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.math.BigDecimal;

@Service
public class WeixinPayService
{
    private static final Logger log;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private UserService userService;
    @Autowired
    private StoreOrderService storeOrderService;
    @Autowired
    private UserRechargeService userRechargeService;

    //attach相当于操作类型，如recharge或者pay_product(消费)
    public Object unifyPay(final String orderId, final String from, final String attach, final String body) {
        //这个uid必须是从user_recharge里面或者store_order里面查出来获取到
        long uid = 0L;
        int payPrice = 0;
        //数据库里面任然是元为单位，但是这里要先转换成以分为单位，所以到时候获取出来的值必须*100
        //之所以要转换成分，是因为调用微信下单接口，规定了必须把单位改为分
        final BigDecimal bigDecimal = new BigDecimal(100);
        //判断是不是消费
        if (BillDetailEnum.TYPE_3.getValue().equals(attach)) {
            //如果是消费的话，肯定会先在store_order表里面存订单数据
            final StoreOrderQueryVo orderInfo = this.storeOrderService.getOrderInfo(orderId, (Long)null);
            if (ObjectUtil.isNull(orderInfo)) {
                throw new EshopException("订单不存在");
            }
            if (orderInfo.getPaid().equals(OrderInfoEnum.PAY_STATUS_1.getValue())) {
                throw new EshopException("该订单已支付");
            }
            if (orderInfo.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) {
                throw new EshopException("该支付无需支付");
            }
            uid = orderInfo.getUid().intValue();
            //将数据库中以元为单位的价格改成以分为单位，所以累乘100
            payPrice = bigDecimal.multiply(orderInfo.getPayPrice()).intValue();
        }
        //否则就是充值
        else {
            //先查user_recharge表，看是否真的是充值过的
            final UserRecharge userRecharge = (UserRecharge)this.userRechargeService.getOne(Wrappers.<UserRecharge>lambdaQuery().eq(UserRecharge::getOrderId, orderId));
            if (userRecharge == null) {
                throw new BusinessException("充值订单不存在");
            }
            if (userRecharge.getPaid().equals(OrderInfoEnum.PAY_STATUS_1.getValue())) {
                throw new EshopException("该订单已支付");
            }
            uid = userRecharge.getUid();
            //将数据库中以元为单位的价格改成以分为单位，所以累乘100
            //这个price是实际支付金额，不包含赠送金额
            payPrice = bigDecimal.multiply(userRecharge.getPrice()).intValue();
        }
        //根据uid获取user，这个uid是上面获取取来的，避免越权
        final ShopUser shopUser = (ShopUser)this.userService.getById((Serializable)uid);
        if (shopUser == null) {
            throw new EshopException("用户错误");
        }
        //获取user的wx用户json信息，具体是城市国家语言头像性别unionId等等
        final WechatUserDto wechatUserDto = shopUser.getWxProfile();
        WxPayService wxPayService = null;
        //keypoint 这里nb，根据来源于APP还是小程序这些，来动态获取出wxPayService(这个是微信的SDK)
        if (AppFromEnum.ROUNTINE.getValue().equals(from)) {
            //小程序支付
            wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.WXAPP);
        }
        else if (AppFromEnum.APP.getValue().equals(from)) {
            //APP支付
            wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.APP);
        }
        else {
            //公众号支付
            wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.WECHAT);
        }
        //创建微信请求类
        final WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
        //设置订单Id，这个Id就是之前user_recharge或者store_order中的orderId（用Hutool雪花算法生成的）
        orderRequest.setOutTradeNo(orderId);
        //设置总价格
        orderRequest.setTotalFee(Integer.valueOf(payPrice));
        //获取当前请求的IP地址
        orderRequest.setSpbillCreateIp(IpUtil.getLocalIP());
        //告知微信回调地址 this.getApiUrl()获取的是移动端api地址  当前这个回调地址接口貌似没有
        orderRequest.setNotifyUrl(this.getApiUrl() + "/api/wechat/notify");
        //body就是controller传过来的，比如H5支付，小程序支付等，类似于备注mark
        orderRequest.setBody(body);
        //设置是消费 controller直接传的BillDetailEnum.TYPE_3.getValue()， pay_product
        orderRequest.setAttach(attach);
        //设置支付的类型
        if (AppFromEnum.WEIXIN_H5.getValue().equals(from)) {
            //H5就是这种
            orderRequest.setTradeType("MWEB");
        }
        else if (AppFromEnum.APP.getValue().equals(from)) {
            orderRequest.setTradeType("APP");
        }
        else {
            //小程序支付就是调用的JSAPI
            orderRequest.setTradeType("JSAPI");
            if (AppFromEnum.ROUNTINE.getValue().equals(from)) {
                orderRequest.setOpenid(wechatUserDto.getRoutineOpenid());
            }
            else {
                orderRequest.setOpenid(wechatUserDto.getOpenid());
            }
        }
        try {
            //用sdk类给微信发请求，创建订单，并返回结果类WxPayMwebOrderResult
            return wxPayService.createOrder(orderRequest);
        }
        catch (WxPayException e) {
            WeixinPayService.log.info("支付错误信息：{}", e.getMessage());
            //注意这里是BusinessException();
            throw new BusinessException(e.getMessage());
        }
    }

    //退货
    public void refundOrder(String orderId, final Integer refundFee) {
        final StoreOrderQueryVo orderInfo = this.storeOrderService.getOrderInfo(orderId, (Long)null);
        //如果是余额支付的，那就不用继续走了
        if (PayTypeEnum.YUE.getValue().equals(orderInfo.getPayType())) {
            return;
        }
        if (orderInfo.getExtendOrderId() != null) {
            orderId = orderInfo.getExtendOrderId();
        }
        final WxPayService wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.WECHAT);
        final WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
        final BigDecimal bigDecimal = new BigDecimal("100");
        final int totalFee = bigDecimal.multiply(orderInfo.getPayPrice()).intValue();
        wxPayRefundRequest.setTotalFee(Integer.valueOf(totalFee));
        wxPayRefundRequest.setOutTradeNo(orderId);
        final String orderSn = IdUtil.getSnowflake(0L, 0L).nextIdStr();
        wxPayRefundRequest.setOutRefundNo(orderSn);
        wxPayRefundRequest.setRefundFee(refundFee);
        wxPayRefundRequest.setNotifyUrl(this.getApiUrl() + "/api/notify/refund");
        try {
            wxPayService.refundV2(wxPayRefundRequest);
        }
        catch (WxPayException e) {
            //记得是调用的WeixinPayService的日志打印，而不是直接e.printStack..()
            WeixinPayService.log.info("退款错误信息：{}", e.getMessage());
            throw new BusinessException(e.getMessage());
        }
    }

    public void entPay(final String openid, final String no, final String userName, final Integer amount) throws WxPayException {
        final WxPayService wxPayService = WxPayConfiguration.getPayService(PayMethodEnum.WECHAT);
        final EntPayRequest entPayRequest = new EntPayRequest();
        entPayRequest.setOpenid(openid);
        entPayRequest.setPartnerTradeNo(no);
        entPayRequest.setCheckName("FORCE_CHECK");
        entPayRequest.setReUserName(userName);
        entPayRequest.setAmount(amount);
        entPayRequest.setDescription("提现");
        entPayRequest.setSpbillCreateIp(IpUtil.getLocalIP());
        wxPayService.getEntPayService().entPay(entPayRequest);
    }

    private String getApiUrl() {
        final String apiUrl = this.redisUtils.getY(ShopKeyUtils.getApiUrl());
        if (StrUtil.isBlank((CharSequence)apiUrl)) {
            throw new EshopException("请配置移动端api地址");
        }
        return apiUrl;
    }

    static {
        log = LoggerFactory.getLogger((Class)WeixinPayService.class);
    }
}
